package com.stone.service.impl;

import cn.hutool.captcha.CaptchaUtil;
import cn.hutool.captcha.CircleCaptcha;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.stone.bean.dto.LoginDTO;
import com.stone.bean.vo.LoginVO;
import com.stone.config.exception.BusinessException;
import com.stone.constant.Const;
import com.stone.entity.SysMenu;
import com.stone.entity.SysUserRole;
import com.stone.mapper.SysUserRoleMapper;
import com.stone.service.LoginService;
import com.stone.service.SysMenuService;
import com.stone.service.SysUserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Slf4j
@Service("loginService")
public class LoginServiceImpl implements LoginService {

    @Resource
    private SysUserService sysUserService;
    @Resource
    private SysMenuService sysMenuService;
    @Resource
    private SysUserRoleMapper sysUserRoleMapper;
    @Resource
    private RedisTemplate redisTemplate;

    @Override
    public String creatImageCode(HttpServletRequest request) {
        CircleCaptcha captcha = CaptchaUtil.createCircleCaptcha(200, 100, 4, 20);
        String code = captcha.getCode();
        // 存储redis
        String clientIP = ServletUtil.getClientIP(request);
        String key = Const.IMAGE_PREFIX + clientIP;
        redisTemplate.opsForValue().set(key, code, 60, TimeUnit.SECONDS);
        String imageBase64 = captcha.getImageBase64();
        return "data:image/png;base64," + imageBase64;
    }

    @Override
    public LoginVO login(LoginDTO dto, HttpServletRequest request) {
        String imageCodeParam = dto.getImageCode().toLowerCase();
        String clientIP = ServletUtil.getClientIP(request);
        Object code = redisTemplate.opsForValue().get(Const.IMAGE_PREFIX + clientIP);
        String imageCode = String.valueOf(code);
        if (StrUtil.isBlank(imageCode) || !imageCode.equalsIgnoreCase(imageCodeParam)) {
            throw new BusinessException("验证码错误");
        } else {
            redisTemplate.delete(Const.IMAGE_PREFIX + clientIP);
        }
        LoginVO logUser = sysUserService.selectByPhoneAndPwd(dto.getPhone(), dto.getPassword());
        if (Objects.isNull(logUser)) {
            throw new BusinessException("未查询到用户数据");
        }
        logUser.setToken(IdUtil.fastSimpleUUID());
        // 查询角色
        List<SysUserRole> roleList = sysUserRoleMapper.selectList(new QueryWrapper<SysUserRole>().eq("user_id", logUser.getId()));
        if (CollUtil.isNotEmpty(roleList)) {
            List<Long> collect = roleList.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
            logUser.setRoleIdList(collect);
        }
        // 角色下的权限
        List<SysMenu> sysMenus = sysMenuService.listByRoleIds(logUser.getRoleIdList());
        if (CollUtil.isNotEmpty(sysMenus)) {
            logUser.setUrlList(sysMenus.stream().filter(ele -> ele.getType() == Const.Menu.button.type && !StrUtil.equals("#", ele.getUrl())).map(ele -> ele.getUrl()).collect(Collectors.toList()));
        }
        sysUserService.saveUserToken(logUser.getId(), logUser.getToken());
        sysUserService.saveIp(logUser.getId(), clientIP);
        redisTemplate.opsForValue().set(Const.AUTH_PREFIX + logUser.getToken(), JSON.toJSONString(logUser), Const.AUTH_EXPIRE, TimeUnit.SECONDS);
        return logUser;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void logout(String token) {
        if (StrUtil.isEmpty(token)) {
            throw new BusinessException("token为空");
        }
        Object o = redisTemplate.opsForValue().get(Const.AUTH_PREFIX + token);
        if (Objects.isNull(o)) {
            log.info("退出登录时，token已过期:{}", token);
            return;
        }
        LoginVO loginVO = JSON.parseObject(o.toString(), LoginVO.class);
        sysUserService.saveUserToken(loginVO.getId(), StrUtil.EMPTY);
        redisTemplate.delete(Const.AUTH_PREFIX + token);
    }

}
